// HTMLEvent.h
#pragma once

#ifndef __DHTML_EVENTS_H__
#define __DHTML_EVENTS_H__

namespace Automation {
namespace IE {

/**
 * AbstractHtmlEvent<T>
 */
template <class T, class EventImplementation>
class AbstractHtmlEvent : public IDispatch
{
protected:
	AbstractHtmlEvent(T* pHandler)
		: m_lRef(0), m_pHandler(pHandler), m_refKeeper(pHandler->GetUnknown()) 
	{
	}

	~AbstractHtmlEvent()
	{
	}

public:
	STDMETHOD_(DWORD, AddRef)()
	{
		return InterlockedIncrement(&m_lRef);
	}

	STDMETHOD_(DWORD, Release)()
	{
		LONG lRef = InterlockedDecrement( &m_lRef );
		if ( lRef != 0 )
			return lRef;

		delete (EventImplementation*)this;
		return 0;
	}

	STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject)
	{
		*ppvObject = NULL;

		if (IsEqualGUID(riid, IID_IUnknown))
			*ppvObject = reinterpret_cast<void**>(this);
		else if (IsEqualGUID(riid, IID_IDispatch))
			*ppvObject = reinterpret_cast<void**>(this);
		else
			return E_NOINTERFACE;

		AddRef();
		return S_OK;
	}

	STDMETHOD(GetTypeInfoCount)(unsigned int FAR* pctinfo)
		{ return E_NOTIMPL; }

	STDMETHOD(GetTypeInfo)(unsigned int iTInfo, LCID  lcid, ITypeInfo FAR* FAR*  ppTInfo)
		{ return E_NOTIMPL; }

	STDMETHOD(GetIDsOfNames)(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgDispId)
		{ return S_OK; }

private:
	LONG volatile m_lRef;

protected:
	T*				  m_pHandler;
	CComPtr<IUnknown> m_refKeeper;

	typedef AbstractHtmlEvent __baseHtmlEvent;
};

/**
 * CHtmlEventSink<T>
 */
template <class T>
class CSingleHtmlEvent : public AbstractHtmlEvent<T, CSingleHtmlEvent<T> >
{
	typedef void (T::*EVENT_FUNCTION)(LPVARIANT pVarResult);
protected:
	CSingleHtmlEvent(T* pHandler, EVENT_FUNCTION pFunc)
		: __baseHtmlEvent(pHandler)
		, m_pFunc(pFunc)
	{
	}

public:
   STDMETHOD(Invoke)(DISPID dispIdMember
					, REFIID riid
					, LCID lcid
					, WORD wFlags
					, DISPPARAMS* pDispParams
					, VARIANT* pVarResult
					, EXCEPINFO* pExcepInfo
					, UINT* puArgErr)
   {
		if (0 != dispIdMember)
		{
			ATLTRACE( _T("SingleHtmlEvent: Invoke invalid dispid = %d\n"), dispIdMember);
		}

		if (m_pHandler) (m_pHandler->*m_pFunc)(pVarResult);
	
		return S_OK;
   }

public:
   static CComPtr<IDispatch> CreateHandler(T* pHandler, EVENT_FUNCTION pFunc)
   {
	   return new CSingleHtmlEvent<T>(pHandler, pFunc);
   }

protected:	
	EVENT_FUNCTION m_pFunc;
};

/**
 * CHtmlEvent<T, GData>
 */
template <class T, class GData = DWORD>
class CHtmlEvent : public AbstractHtmlEvent<T, CHtmlEvent<T, GData> >
{
	typedef void (T::*EVENT_FUNCTION)(GData data, DISPID idEvent, LPVARIANT pVarResult);

protected:
	CHtmlEvent(T* pHandler, EVENT_FUNCTION pFunc, GData data)
		: __baseHtmlEvent(pHandler)
		, m_data(data)
		, m_pFunc(pFunc)
	{
	}

public:
   STDMETHOD(Invoke)(DISPID dispIdMember
					, REFIID riid
					, LCID lcid
					, WORD wFlags
					, DISPPARAMS* pDispParams
					, VARIANT* pVarResult
					, EXCEPINFO* pExcepInfo
					, UINT* puArgErr)
   {
	  if (m_pHandler) (m_pHandler->*m_pFunc)(m_data, dispIdMember, pVarResult);
	
      return S_OK;
   }

public:
	static CComPtr<IDispatch> CreateHandler(T* pHandler, EVENT_FUNCTION pFunc, GData data)
	{
		return new CHtmlEvent<T, GData>(pHandler, pFunc, data);
	}

protected:	
	GData			m_data;
	EVENT_FUNCTION	m_pFunc;
};

/**
 * CHtmlEvent<T, GData>
 */
template <class T, class Data1 = DWORD, class Data2 = DWORD>
class CHtmlEvent2 : public AbstractHtmlEvent<T, CHtmlEvent2<T, Data1> >
{
	typedef void (T::*EVENT_FUNCTION)(Data1 data1, Data2 data2, DISPID idEvent, LPVARIANT pVarResult);

protected:
	CHtmlEvent2(T* pHandler, EVENT_FUNCTION pFunc, Data1 data1, Data2 data2)
		: __baseHtmlEvent(pHandler)
		, m_data1(data1)
		, m_data2(data2)
		, m_pFunc(pFunc)
	{
	}

public:
   STDMETHOD(Invoke)(DISPID dispIdMember
					, REFIID riid
					, LCID lcid
					, WORD wFlags
					, DISPPARAMS* pDispParams
					, VARIANT* pVarResult
					, EXCEPINFO* pExcepInfo
					, UINT* puArgErr)
   {
	  if (m_pHandler) (m_pHandler->*m_pFunc)(m_data1, m_data2, dispIdMember, pVarResult);
	
      return S_OK;
   }

public:
	static CComPtr<IDispatch> CreateHandler(T* pHandler, EVENT_FUNCTION pFunc, Data1 data1, Data2 data2)
	{
		return new CHtmlEvent2<T, Data1, Data2>(pHandler, pFunc, data1, data2);
	}

protected:	
	Data1			m_data1;
	Data2			m_data2;
	EVENT_FUNCTION	m_pFunc;
};

}; //namespace IE
}; //namespace Automation

#endif __DHTML_EVENTS_H__